package com.infore.remoteControl.service.impl;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.infore.remoteControl.constant.Constants;
import com.infore.remoteControl.constant.ProtocolConstants;
import com.infore.remoteControl.constant.ResultConstants;
import com.infore.remoteControl.entity.protocolEntity.Protocol;
import com.infore.remoteControl.entity.protocolEntity.ProtocolData;
import com.infore.remoteControl.entity.protocolEntity.ProtocolType;
import com.infore.remoteControl.enums.ResultStatus;
import com.infore.remoteControl.repository.IProtocolRepository;
import com.infore.remoteControl.repository.IProtocolTypeRepository;
import com.infore.remoteControl.select.QueryDispatcher;
import com.infore.remoteControl.select.dto.QueryParamDto;
import com.infore.remoteControl.select.util.QueryUtil;
import com.infore.remoteControl.service.IFileService;
import com.infore.remoteControl.service.IProtocolService;
import com.infore.remoteControl.util.MyBeanUtils;
import com.infore.remoteControl.util.PageUtil;
import com.infore.remoteControl.util.ResultEntity;
import com.infore.remoteControl.util.UUIDGenerator;

@Service
public class ProtocolServiceImpl implements IProtocolService {

	@Autowired
	IProtocolRepository iProtocolRepository;
	
	@Autowired
	IProtocolTypeRepository iProtocolTypeRepository;

	@Autowired
	EntityManager entityManager;
	
	@Autowired
	QueryDispatcher queryDispatcher;
	
	@Autowired
	IFileService iFileService;
	
	/**
	 * CriteriaBuilder接口：用于构造标准查询、复合条件、表达式、排序等。可以通过createQuery的方式获取CriteriaQuery实例。
	 * CriteriaQuery接口：代表一个specific的顶层查询对象，它包含着查询的各个部分，比如：select 、from、where、group by、order by。
	 * Root接口：代表Criteria查询的根对象，定义了实体类型
	 */
	@Override
	public ResultEntity query(QueryParamDto queryParamDto) {
		if (queryParamDto == null) { // 无查询条件
			return new ResultEntity(ResultStatus.SUCCESS, Constants.QUERY_SUCCESS)
					.setDataList(iProtocolRepository.findByOrderByNumbers());
		} else if (queryParamDto.getPageObject() != null && queryParamDto.getQueryCondition() == null
				&& queryParamDto.getAssembleCondition() == null && queryParamDto.getAssembleSql() == null) { // 无查询条件只分页
			return new ResultEntity(ResultStatus.SUCCESS, Constants.QUERY_SUCCESS).setPage(PageUtil.pageConvert(
					iProtocolRepository.findByOrderByNumbers(queryParamDto.getPageObject().getPageRequest())));
		} else if (StringUtils.isBlank(queryParamDto.getAssembleSql().getSelect())) { // 不指定任何字段
			return this.queryAll(queryParamDto);
		}
		return queryDispatcher.dispatcher(queryParamDto, Protocol.class);
	}
	
	@Override
	public ResultEntity edit(Protocol protocol) {
		// 验证标识
		boolean flag = true;
		// 首先判断协议ID是否存在,如果存在则需要根据传入的协议ID查询实体,再赋值
		if (StringUtils.isNotBlank(protocol.getProtocolId())) {
			// 这里使用findById而不使用existsById查询，是因为existsById返回的是boolean型,而下面判断是需要实体对象
			Protocol oldProtocol = iProtocolRepository.findById(protocol.getProtocolId()).orElse(null);
			// 判断根据传入的协议ID查询该数据是否存在
			if (oldProtocol != null) {
				// 查询原始对象将非空字段复制到保存对象的空字段上,避免更新操作的时候将空字段也修改了
				// MyBeanUtils.copyNonNullProperties匹配只针对单个对象,对于集合需要遍历匹配(协议数据为多个)
				Set<ProtocolData> oldProtocolData = oldProtocol.getProtocolData();
				for (ProtocolData oProtocolData : oldProtocolData) {
					Set<ProtocolData> newProtocolData = protocol.getProtocolData();
					for (ProtocolData nProtocolData : newProtocolData) {
						// 需要匹配旧的对象与新的对象内唯一ID，以免更新数据有误
						if (nProtocolData.getId().equals(oProtocolData.getId())) {
							MyBeanUtils.copyNonNullProperties(nProtocolData, oProtocolData);
						}
					}
				}
				protocol.setProtocolData(oldProtocolData);
				// 匹配协议类型空字段情况
				ProtocolType oldProtocolType = oldProtocol.getProtocolType();
				MyBeanUtils.copyNonNullProperties(protocol.getProtocolType(), oldProtocolType);
				protocol.setProtocolType(oldProtocolType);
				// 匹配协议内部属性字段
				MyBeanUtils.copyNonNullProperties(protocol, oldProtocol);
				protocol = oldProtocol;
			} else {
				// 数据对象不存在
				return new ResultEntity(ResultStatus.ERROR, ResultConstants.NO_SUCH_OBJECT);
			}
		} else {
			// 需要重新设置协议数据,因为新增时协议数据中关联的protocol为null,而表设计及业务方面必须此属性不为空
			Set<ProtocolData> protocolData = protocol.getProtocolData();
			if (protocolData != null) {
				for (ProtocolData proData : protocolData) {
					proData.setProtocol(protocol);
				}
			}
		}
		// 验证协议
		ResultEntity resultEntity = validate(protocol, flag);
		if (resultEntity.getCode() == 1) {
			return resultEntity;
		}
		// 保存
		iProtocolRepository.saveAndFlush(protocol);
		return new ResultEntity(ResultStatus.SUCCESS, Constants.DEAL_SUCCESS);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ResultEntity editProtocol(Protocol protocol) {
		// 验证标识
		boolean flag = true;
		// 重新set协议类型对象值
		ProtocolType protocolType = new ProtocolType();
		protocolType.setProtocolTypeId(protocol.getProtocolTypeIds());
		protocolType.setCode(protocol.getProtocolTypeCode());
		protocolType.setName(protocol.getProtocolTypeName());
		protocol.setProtocolType(protocolType);
		// 重新set协议数据对象值
		Set<ProtocolData> list = new HashSet<ProtocolData>();
		List<String> publishedDates = protocol.getPublishedDates();
		List<String> protocolDataIds = protocol.getProtocolDataIds();
		List<MultipartFile> files = protocol.getFiles();
		if (publishedDates != null && publishedDates.size() != 0) {
			for (int i = 0; i < publishedDates.size(); i++) {
				// 上传文件
				String filePath = "";
				if (files != null) { // 编辑状态下需要此判断
					if (files.get(i) != null) {
						ResultEntity fileResultEntity = iFileService.uploadFile(files.get(i), true);
						// 获取上传文件所在的路径
						if (fileResultEntity.getCode() == 0) {
							Map<String, Object> map = (Map<String, Object>) fileResultEntity.getContent();
							filePath = map.get("info").toString();
						}
					}
				}
				ProtocolData protocolData = new ProtocolData();
				protocolData.setId(protocolDataIds.get(i).toString());
				protocolData.setPublishedDate(publishedDates.get(i).toString());
				protocolData.setUrl(filePath);
				list.add(protocolData);
			}
		}
		protocol.setProtocolData(list);
		// 首先判断协议ID是否存在,如果存在则需要根据传入的协议ID查询实体,再赋值
		if (StringUtils.isNotBlank(protocol.getProtocolId())) {
			// 这里使用findById而不使用existsById查询，是因为existsById返回的是boolean型,而下面判断是需要实体对象
			Protocol oldProtocol = iProtocolRepository.findById(protocol.getProtocolId()).orElse(null);
			// 判断根据传入的协议ID查询该数据是否存在
			if (oldProtocol != null) {
				// 查询原始对象将非空字段复制到保存对象的空字段上,避免更新操作的时候将空字段也修改了
				// MyBeanUtils.copyNonNullProperties匹配只针对单个对象,对于集合需要遍历匹配(协议数据为多个)
				Set<ProtocolData> oldProtocolData = oldProtocol.getProtocolData();
				// 编辑时如果新增协议版本文件时需要另初始化一个Set<ProtocolData>,并将原始的数据添加进去
				Set<ProtocolData> oldProtocolDatas = new HashSet<ProtocolData>();
				oldProtocolDatas.addAll(oldProtocolData);
				for (ProtocolData oProtocolData : oldProtocolData) {
					Set<ProtocolData> newProtocolData = protocol.getProtocolData();
					for (ProtocolData nProtocolData : newProtocolData) {
						// 需要匹配旧的对象与新的对象内唯一ID，以免更新数据有误
						if (nProtocolData.getId().equals(oProtocolData.getId())) {
							MyBeanUtils.copyNonNullProperties(nProtocolData, oProtocolData);
						} /*else if (nProtocolData.getId().equals(oProtocolData.getId()) && 
								!nProtocolData.getPublishedDate().equals(oProtocolData.getPublishedDate())) {
							// 对编辑的协议版本设置UUID与关联的协议对象,否则会报字段空错误
							System.out.println(1);
							nProtocolData.setId(UUIDGenerator.getUUID());
							nProtocolData.setProtocol(protocol);
							oldProtocolDatas.add(nProtocolData);
						}*/ else if ("".equals(nProtocolData.getId())) { // 新增的版本协议
							nProtocolData.setId(UUIDGenerator.getUUID());
							nProtocolData.setProtocol(protocol);
							oldProtocolDatas.add(nProtocolData);
						}
					}
				}
				protocol.setProtocolData(oldProtocolDatas);
				// 匹配协议类型空字段情况
				ProtocolType oldProtocolType = oldProtocol.getProtocolType();
				MyBeanUtils.copyNonNullProperties(protocol.getProtocolType(), oldProtocolType);
				protocol.setProtocolType(oldProtocolType);
				// 匹配协议内部属性字段
				MyBeanUtils.copyNonNullProperties(protocol, oldProtocol);
				protocol = oldProtocol;
				flag = false;
			} else {
				// 数据对象不存在
				return new ResultEntity(ResultStatus.ERROR, ResultConstants.NO_SUCH_OBJECT);
			}
		} else {
			// 必须重新设置协议数据内的协议对象
			if (list != null) {
				for (ProtocolData proData : list) {
					proData.setProtocol(protocol);
				}
			}
		}
		// 验证协议
		ResultEntity resultEntity = validate(protocol, flag);
		if (resultEntity.getCode() == 1) {
			return resultEntity;
		}
		// 保存
		iProtocolRepository.saveAndFlush(protocol);
		return new ResultEntity(ResultStatus.SUCCESS, Constants.DEAL_SUCCESS);
	}

	@Override
	public ResultEntity delete(String ids) {
		// 判断所传入的字符串是否包含逗号,如果包含则表示是删除多条记录
		if (ids.contains(",")) {
			List<String> idList = Arrays.asList(ids.split(","));
			iProtocolRepository.deleteByProtocolIdIn(idList);
		} else {
			iProtocolRepository.deleteById(ids);
		}
		return new ResultEntity(ResultStatus.SUCCESS, Constants.DELETE_SUCCESS);
	}

	@Override
	public ResultEntity validate(Protocol protocol, boolean flag) {
		// 新增时对协议进行校验判断是否已存在
		if (flag) {
			List<Protocol> proNameList = iProtocolRepository.findByName(protocol.getName());
			if (proNameList != null && proNameList.size() != 0) {
				return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NAME_EXISTS);
			}
			List<Protocol> proCodeList = iProtocolRepository.findByCode(protocol.getCode());
			if (proCodeList != null && proCodeList.size() != 0) {
				return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_CODE_EXISTS);
			}
			Protocol numberProtocol = iProtocolRepository.findByNumbers(protocol.getNumbers());
			if (numberProtocol != null) {
				return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NUMBERS_EXISTS);
			}
		}
		// 验证协议
		if (StringUtils.isBlank(protocol.getCode())) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_CODE_NULL);
		} else if (protocol.getCode().length() > 10) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_CODE_LENGTH);
		}
		if (StringUtils.isBlank(protocol.getName())) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NAME_NULL);
		} else if (protocol.getName().length() > 20) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NAME_LENGTH);
		}
		if (StringUtils.isBlank(protocol.getNumbers())) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NUMBERS_NULL);
		} else if (protocol.getNumbers().length() > 20) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOL_NUMBERS_LENGTH);
		}
		// 验证协议类型
		ProtocolType protocolType = protocol.getProtocolType();
		if (StringUtils.isBlank(protocolType.getCode())) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLTYPE_CODE_NULL);
		} else if (protocolType.getCode().length() > 10) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLTYPE_CODE_LENGTH);
		}
		if (StringUtils.isBlank(protocolType.getName())) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLTYPE_NAME_NULL);
		} else if (protocolType.getCode().length() > 10) {
			return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLTYPE_NAME_LENGTH);
		}
		// 验证协议数据
		Set<ProtocolData> protocolData = protocol.getProtocolData();
		if (protocolData != null) { 
			for (ProtocolData proData : protocolData) {
				if (proData.getPublishedDate() == null) {
					return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLDATA_DATE_NULL);
				} else if (proData.getUrl().length() > 500) {
					return new ResultEntity(ResultStatus.ERROR, ProtocolConstants.PROTOCOLDATA_URL_LENGTH);
				}
			}
		}
		return new ResultEntity(ResultStatus.SUCCESS, ProtocolConstants.PROTOCOL_SUCCESS);
	}
	
	// 不指定字段的通用查询
	@SuppressWarnings("serial")
	public ResultEntity queryAll(QueryParamDto queryParamDto) {
		return queryParamDto.getPageObject() != null // 判断是否分页
				? ResultEntity.page(iProtocolRepository.findAll(new Specification<Protocol>() {
					@Override
					public Predicate toPredicate(Root<Protocol> root, CriteriaQuery<?> query,
							CriteriaBuilder criteriaBuilder) {
						// 构建查询条件
						List<Predicate> predicates = QueryUtil.createQueryCondition(queryParamDto,
								criteriaBuilder, root);
						// 组装查询条件
						Predicate predicate = QueryUtil.assembleCondition(queryParamDto, criteriaBuilder,
								predicates);
						// 组装SQL
						QueryUtil.assembleSql(queryParamDto, criteriaBuilder, query, root, predicate);
						return query.getRestriction();
					}
				}, queryParamDto.getPageObject().getPageRequest()))
				: new ResultEntity(ResultStatus.SUCCESS, Constants.QUERY_SUCCESS)
						.setDataList(iProtocolRepository.findAll(new Specification<Protocol>() {
							@Override
							public Predicate toPredicate(Root<Protocol> root, CriteriaQuery<?> query,
									CriteriaBuilder criteriaBuilder) {
								// 构建查询条件
								List<Predicate> predicates = QueryUtil.createQueryCondition(queryParamDto,
										criteriaBuilder, root);
								// 组装查询条件
								Predicate predicate = QueryUtil.assembleCondition(queryParamDto, criteriaBuilder,
										predicates);
								// 组装SQL
								QueryUtil.assembleSql(queryParamDto, criteriaBuilder, query, root,
										predicate);
								return query.getRestriction();
							}
						}));
	}

}
